package com.netty.example.time;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.ByteBuffer;

public class TimeServerHandler extends ChannelInboundHandlerAdapter {
    /*
    1.channelActive()当建立连接并准备产生流量时，将调用该方法。
    让我们写一个代表该方法当前时间的32位整数。
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        /*
        2.要发送新消息，我们需要分配一个新缓冲区，其中将包含该消息。我们将要编写一个32位整数，
        因此我们需要一个ByteBuf容量至少为4个字节的。ByteBufAllocator通过获取电流
        ChannelHandlerContext.alloc()并分配一个新的缓冲区。
         */
        final ByteBuf time = ctx.alloc().buffer(4);

        time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
        /*
        3.和往常一样，我们构建一个消息。

        但是，等等，flip() 在哪里？我们不是曾经java.nio.ByteBuffer.flip()在NIO中发送消息之前调用吗？
        ByteBuf没有这样的方法，因为它有两个指针；一个用于读取操作，另一个用于写入操作。
        当您向某处写入内容时，作家索引会增加，ByteBuf而读者索引不会改变。读者索引和作者索引分别表示消息
        的开始和结束位置。

        相反，NIO缓冲区没有提供一种干净的方法来确定消息内容的开始和结束位置，而无需调用flip方法。
        当您忘记翻转缓冲区时会遇到麻烦，因为将不会发送任何内容或发送不正确的数据。在Netty中不会发生
        这样的错误，因为我们对于不同的操作类型有不同的指针。您会发现，适应它会使您的生活变得更加轻松
        -无需翻身的生活！

        要注意的另一点是ChannelHandlerContext.write()（和writeAndFlush()）
        方法返回一个ChannelFuture。AChannelFuture表示尚未发生的I / O操作。这意味着可能尚未执行
        任何请求的操作，因为Netty中的所有操作都是异步的。例如，以下代码甚至在发送消息之前就可能关闭连接：
            Channel ch = ...;
            ch.writeAndFlush(message);
            ch.close();
         因此，您需要在close()方法ChannelFuture完成后调用该方法，该write()方法由该方法返回，
         并在完成写操作后通知其侦听器。请注意，close()也可能不会立即关闭连接，并且返回ChannelFuture。
         */
        ChannelFuture f = ctx.writeAndFlush(time);
        /*
        4.当写请求完成时，我们如何得到通知？这就像将a添加ChannelFutureListener到return
        一样简单ChannelFuture。在这里，我们创建了一个新的匿名操作ChannelFutureListener，
        该匿名Channel操作将在操作完成时关闭。
        另外，您可以使用预定义的侦听器简化代码：
            f.addListener(ChannelFutureListener.CLOSE);
        要测试我们的时间服务器是否按预期工作，可以使用UNIXrdate命令：
            $ rdate -o <port> -p <host>
        其中<port>是您在main()方法中指定的端口号，<host>通常为localhost。
         */
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                assert f == channelFuture;
                ctx.close();
            }
        });
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
